<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Knowledge Graph with D3.js</title>
    <style>
        body {
            margin: 0;
            font-family: Arial, sans-serif;
        }

        svg {
            width: 100%;
            height: 100vh;
            border: 1px solid #ccc;
        }

        .node {
            stroke: #fff;
            stroke-width: 2px;
            cursor: pointer;
        }

        .link {
            stroke: #999;
            stroke-opacity: 0.6;
        }

        .label {
            font-size: 12px;
            fill: #333;
            pointer-events: none; /* 防止标签遮挡节点交互 */
        }
    </style>
</head>
<body>
    <svg></svg>
    <script src="https://d3js.org/d3.v7.min.js"></script>
    <script>
    // 定义图数据
    const graph = {
      nodes: [
        { id: "A", name: "Node A" },
        { id: "B", name: "Node B" },
        { id: "C", name: "Node C" },
        { id: "D", name: "Node D" }
      ],
      links: [
        { source: "A", target: "B" },
        { source: "B", target: "C" },
        { source: "C", target: "D" },
        { source: "D", target: "A" }
      ]
    };

    // 获取 SVG 元素
    const svg = d3.select("svg");
    
    // 获取SVG容器的实际尺寸
    const width = window.innerWidth;
    const height = window.innerHeight;
    
    // 设置SVG的尺寸
    svg.attr("width", width)
       .attr("height", height);

    // 定义力模拟
    const simulation = d3.forceSimulation(graph.nodes)
      .force("link", d3.forceLink(graph.links).id(d => d.id))
      .force("charge", d3.forceManyBody().strength(-200)) // 节点间的排斥力
      .force("center", d3.forceCenter(width / 2, height / 2)) // 将图居中
      .force("collision", d3.forceCollide().radius(50)); // 防止节点重叠

    // 创建连线
    const link = svg.append("g")
      .attr("class", "links")
      .selectAll("line")
      .data(graph.links)
      .enter().append("line")
      .attr("class", "link");

    // 创建节点
    const node = svg.append("g")
      .attr("class", "nodes")
      .selectAll("circle")
      .data(graph.nodes)
      .enter().append("circle")
      .attr("class", "node")
      .attr("r", 10)
      .attr("fill", "#69b3a2")
      .call(d3.drag() // 允许拖拽节点
        .on("start", dragStarted)
        .on("drag", dragged)
        .on("end", dragEnded)
      );

    // 添加节点标签
    const label = svg.append("g")
      .attr("class", "labels")
      .selectAll("text")
      .data(graph.nodes)
      .enter().append("text")
      .attr("class", "label")
      .text(d => d.name);

    // 力模拟的 tick 事件
    simulation.on("tick", () => {
      link
        .attr("x1", d => d.source.x)
        .attr("y1", d => d.source.y)
        .attr("x2", d => d.target.x)
        .attr("y2", d => d.target.y);

      node
        .attr("cx", d => d.x)
        .attr("cy", d => d.y);

      label
        .attr("x", d => d.x + 12)
        .attr("y", d => d.y + 4);
    });

    // 拖拽事件处理函数
    function dragStarted(event, d) {
      if (!event.active) simulation.alphaTarget(0.3).restart();
      d.fx = d.x;
      d.fy = d.y;
    }

    function dragged(event, d) {
      d.fx = event.x;
      d.fy = event.y;
    }

    function dragEnded(event, d) {
      if (!event.active) simulation.alphaTarget(0);
      d.fx = null;
      d.fy = null;
    }
    </script>
</body>
</html>